package com.cskaoyan.javase._2inner._6lambda._1basic;

/**
 * 接下来,讲解一下Lambda表达式的进阶使用,主要就是格式的简化。
 * Lambda表达式能够简化的前提是：
 *      它是功能接口的子类对象,功能接口中有且只有一个必须要实现的抽象方法！
 *      而且注意Lambda表达式的简化,不能引起歧义,否者不可以简化.
 * 语法:
 * (形参列表) -> {
 *  // 方法体
 * }
 *
 * 1.(形参列表),这里能不能简化呢?
 *      当然是可以的
 *      形参列表中的数据类型是不必要的
 *      因为写与不写,数据类型肯定都是一样的
 *      所以可以省略形参列表中的数据类型
 *
 *      特殊情况下,如果形参列表就只有一个参数
 *      那么小括号也可以省略
 *      直接写形参名即可
 *
 *      但是如果方法是无参的,空的小括号必须写出来,无法省略.
 *
 * 2.{},大括号可以省略吗?
 *      当然是可以的
 *      参考"if,for,while"等结构,在执行语句仅一条时,可以省略大括号
 *      那么Lambda表达式在重写方法体的语句仅有一条时,也可以省略大括号
 *
 *      特殊的,当仅有一条的语句,是方法的返回值语句时
 *      可以连带大括号,return关键字一起被省略
 *
 * 在Lambda表达式的简化中,最需要被去掉的应该是大括号,大括号增加了代码层次,
 *      一点不上流,一点不高端,不符合Lambda表达式的身份
 * 但是在实际开发中,不太可能一句话就能够重写完抽象方法
 * 所以在Java中,提供了"方法引用"来简化Lambda表达式的实现
 *
 * Lambda表达式的本质是用一个实现方法来表示功能接口中抽象方法的实现,更进一步说,方法重写是Lambda表达式的实质
 * 所以Lambda表达式的语法中,可以用"一个已经存在的,已经实现好的方法"直接作为Lambda表达式的实现
 * 将Lambda表达式的实现直接指向一个方法,这种语法称之为"方法引用"
 *
 * 首先,我们来探究一下,什么样的已经存在的方法,可以作为Lambda表达式的实现?
 * 回顾一下方法重写的语法
 * 抽象方法的语法:
 * 返回值类型 方法名(形参列表);
 *
 * 对于能够作为Lambda表达式实现的已实现方法:
 *      1.修饰符列表是没有要求的,有访问权限即可
 *          static方法或者非static都是无所谓的
 *      2.返回值类型是有要求的
 *          要求"兼容":
 *          基本数据类型和void必须一模一样
 *          引用数据类型,该方法的返回值类型可以是一样的,也可以是子类类型
 *      3.方法名,无所谓,什么都行
 *      4.形参列表,必须要求完全一致
 *      5.方法体,无所谓,都行.
 *
 * 方法引用的语法有两种:
 * 1.不省略"->"的写法
 * (形参列表) -> 调用已存在的方法(形参列表);
 * 解释:
 *      a.调用已存在的方法
 *      指的是在这个写Lambda表达式的位置,去调用那个已实现的方法
 *      如果是静态方法 用类名点调用
 *      如果是成员方法 用对象名点调用
 *      注意:
 *          这里虽然说是调用已存在方法
 *          但实际上后面写的是形参列表
 *          具体传参需要等到调用方法时传入
 *      b.上述语法左右两边的形参列表是一致的
 *       表示将Lambda表达式的形参传递给已实现的方法.
 *
 * 2.更常用的,更简洁的,省略形参列表和"->"的写法:
 * 父接口引用 对象名 = 方法的归属者::方法名;
 * 解释:
 *      什么叫方法的归属者?
 *      static方法属于类,写类名
 *      成员方法属于对象,写对象/匿名对象
 *
 * 以上Lambda表达式的使用，就基本结束了，在后面的学习中，
 * 我们会结合具体场景再去使用Lambda表达式。
 * 这里我们就不再赘述了，大家先了解语法就可以了。
 *
 * @since 15:55
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        INoReturnTwoParam ip1 = (int num1, int num2) -> {
            System.out.println(num1 + num2);
        };
        ip1.test(1, 1);
        // 以上Lambda表达式简化
        INoReturnTwoParam ip2 = (a, b) -> {
            System.out.println(a - b);
        };
        ip2.test(1, 1);

        INoReturnOneParam ip3 = a -> {
            System.out.println(a + "hello world!");
        };
        ip3.test(100);

        // 下面开始简化大括号
        INoReturnNoParam ip4 = () -> System.out.println("我喜欢你!");
        ip4.test();

        IHasReturnNoParam ip5 = () -> 0;

        IHasReturnTwoParam ip6 = (a, b) -> a + b;
        System.out.println(ip6.test(10, 10));

        // 使用方法引用,创建INoReturnNoParam的子类对象
        INoReturnNoParam ip7 = () -> myTest();
        ip7.test();

        // 使用方法引用,创建IA的子类对象
        IA ia = a -> new Demo().myTest2(a);
        Object o = ia.test(4396);
        if (o instanceof Student) {
            ((Student) o).study();
        }

        // 使用简化方法引用,创建INoReturnNoParam的子类对象
        INoReturnNoParam ip8 = Demo::myTest;
        ip8.test();

        // 使用简化方法引用,创建IA的子类对象
        IA ia2 = new Demo()::myTest2;
        Object o2 = ia2.test(4399);
        if (o2 instanceof Student) {
            ((Student) o2).study();
        }
    }

    // 写一个方法,作为功能接口INoReturnNoParam的实现
    public static void myTest() {
        System.out.println("6666");
        System.out.println("6666");
        System.out.println("6666");
    }

    // 写一个方法,作为功能接口IA的实现
    public Student myTest2(int a) {
        System.out.println("7777");
        System.out.println("7777");
        System.out.println("7777");
        System.out.println(a);
        return new Student();
    }


}

class Student {
    public void study() {
        System.out.println("学习使我快乐!");
    }
}

@FunctionalInterface
interface IA {
    Object test(int a);
}

//无返回值无参数的功能接口
@FunctionalInterface
interface INoReturnNoParam {
    void test();
}

//无返回值有一个参数的功能接口
@FunctionalInterface
interface INoReturnOneParam {
    void test(int a);
}

//无返回值两个参数的功能接口
@FunctionalInterface
interface INoReturnTwoParam {
    void test(int a, int b);
}

//有返回值无参数的功能接口
@FunctionalInterface
interface IHasReturnNoParam {
    int test();
}

//有返回值一个参数的功能接口
@FunctionalInterface
interface IHasReturnOneParam {
    int method(int a);
}

//有返回值两个参数的功能接口
@FunctionalInterface
interface IHasReturnTwoParam {
    int test(int a, int b);
}